/*
 * YAFFS: Yet Another Flash File System. A NAND-flash specific file system.
 *
 * Copyright (C) 2002-2011 Aleph One Ltd.
 *   for Toby Churchill Ltd and Brightstar Engineering
 *
 * Created by Charles Manning <charles@aleph1.co.uk>
 * Acknowledgements:
 * Luc van OostenRyck for numerous patches.
 * Nick Bane for numerous patches.
 * Nick Bane for 2.5/2.6 integration.
 * Andras Toth for mknod rdev issue.
 * Michael Fischer for finding the problem with inode inconsistency.
 * Some code bodily lifted from JFFS
 *
 * DEFY: Deniable Encrypted Functionality on YAFFS
 * 
 * Created by Malcolm Chase Smith, LT, USN <mcsmith@nps.edu>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 as
 * published by the Free Software Foundation.
 */

#include <linux/skbuff.h>
#include <linux/delay.h>
#include <linux/inet.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/spinlock.h>
#include <linux/spinlock_types.h>
#include <linux/types.h>
#include <linux/unistd.h>
#include <linux/syscalls.h>
#include <linux/kmod.h>
#include "linux/mtd/mtd.h"
#include <asm/uaccess.h>
#include "yaffs_defy.h"
#include "yaffs_guts.h"
#include "yaffs_nand.h"
#include "yaffs_attribs.h"
#include "yaffs_mtdif2.h"
#include "yaffs_ioctl.h"
#include "yaffs_linux.h"
#include "yaffs_bitmap.h"
#include "yaffs_summary.h"
#include "yaffs_getblockinfo.h"
#include "yaffs_yaffs2.h"

// DEFY: These should all go in a yaffs_dev once I figure out how
//         to derive it from the root directory
// internal variables
struct yaffs_dev *mydev;
int prev_level_num = -1;
int next_level_num = 0;
int print_indent = 0;
int print_break = 0;
char new_level_pass[DEFY_ID_LENGTH + 1];
char new_level_name[DEFY_ID_LENGTH + 1];

// forward declarations
int defy_find_alloc_block(struct yaffs_dev *dev);
int defy_destroy_level(struct yaffs_dev *dev, struct defy_level_info *level);
static ssize_t device_read(struct file *filp, char __user *buffer,
                           size_t length, loff_t *offset);
static ssize_t device_write(struct file *filp, const char __user *buff,
                            size_t len, loff_t *off);
long device_ioctl(struct file *filep, unsigned int cmd,
                  unsigned long arg);
int defy_write_cache(struct yaffs_dev *dev, struct defy_level_info *level);
void defy_write_marker(struct yaffs_dev *dev,
		       const struct defy_level_info *level);
void defy_format_level_oh(struct yaffs_dev *dev, struct yaffs_obj *root,
			  struct yaffs_obj_hdr *oh);

// IOCTL variables
int dev_major;
struct cdev cdev;
struct class *dev_class;
char msg[18];
struct file_operations fops = {
    .read = device_read,
    .write = device_write,
    .unlocked_ioctl = device_ioctl,
};

void defy_printk_indent(void){
    int i;
    for(i = 0; i < print_indent + 1; i++)
	printk("  ");
}	

void defy_printk(const char *input, const int indent) {
    int i;
    if(indent != END_F)
	print_indent++;
    for(i = 0; i < print_indent; i++)
	printk("  ");
    if(indent == BEGIN_F) {
	print_break = 0;
	printk("> ");
    } else if (indent == END_F)
	printk("< ");
    printk("%s", input);
    printk("\n");
    if(indent != BEGIN_F)
	print_indent--;
}	

void defy_printk_break(void) {
    int i;
    print_indent++;
    for(i = 0; i < print_indent; i++)
	printk("  ");
    printk("Break Point %d", print_break++);
    print_indent--;
    printk("\n");
}	

void print_hex(const u8 *input, const int length) {
    int i;
    if(!input) {
	defy_printk("NULL print_hex input", IN_F);
	return;
    }
    print_indent++;
    defy_printk_indent();
    if(length > DEFY_KEY_LENGTH) {
	for(i = 0; i < DEFY_KEY_LENGTH; i++) {
	    if(input[i] < 16)
		printk("0");
	    printk("%x", input[i]);
	}
	printk("\n");
	print_indent--;
	print_hex(input + DEFY_KEY_LENGTH, length - DEFY_KEY_LENGTH);
    } else {
	for(i = 0; i < length; i++) {
	    if(input[i] < 16)
		printk("0");
	    printk("%x", input[i]);
	}
	printk("\n");
	print_indent--;
    }
}

void print_block_info(const struct yaffs_block_info *bi, int chunk_id) {
    printk("Block Information for Chunk %d\n", chunk_id);
    printk(" soft_del_pages: %d\n", bi->soft_del_pages);
    printk(" pages_in_use: %d\n", bi->pages_in_use);
    printk(" block_state: %d\n", bi->block_state);
    printk(" needs_retiring: %d\n", bi->needs_retiring);
    printk(" skip_erased_check: %d\n", bi->skip_erased_check);
    printk(" gc_prioritise: %d\n", bi->gc_prioritise);
    printk(" chunk_error_strikes: %d\n", bi->chunk_error_strikes);
    printk(" has_summary: %d\n", bi->has_summary);
    printk(" hash_shrink_hdr: %d\n", bi->has_shrink_hdr);
    printk(" seq_number: %u\n", bi->seq_number);
}

void defy_hash(const char *input, const int input_l,
               const char *key, const int key_l, u8 *hash) {
    int TVMEM_NUM = 1;
    struct scatterlist sg[TVMEM_NUM];
    struct crypto_hash *tfm;
    struct hash_desc desc;
    u8 *tvmem[TVMEM_NUM];
    int i;
    
    // Initialize hash
    tfm = crypto_alloc_hash(DEFY_HASH_FUNCTION, 0, CRYPTO_ALG_ASYNC);
    if (IS_ERR(tfm)){
        printk(KERN_INFO "Error allocating hash\n");
        return;
    }
    
    desc.tfm = tfm;
    desc.flags = 0;
    
    // Allocate pages
    for(i = 0; i < TVMEM_NUM; i++){
        tvmem[i] = (void *)__get_free_page(GFP_KERNEL);
        if(!tvmem[i]){
            printk(KERN_INFO "Could not allocate free page\n");
        }
    }
    
    // Initialize scatterlists
    sg_init_table(sg, TVMEM_NUM);
    // Set scatterlist memory
    for(i = 0; i < TVMEM_NUM; i++) {
        sg_set_buf(sg + i, tvmem[i], PAGE_SIZE);
        memset(tvmem[i], 0xFF, PAGE_SIZE);
    }
    
    // Set key
    if(crypto_hash_setkey(tfm, key, key_l)) {
        printk("Key setting failed with flags %x\n",
               crypto_hash_get_flags(tfm));
    }
    
    // Move input to page
    memcpy(tvmem[0], input, input_l);
    // Run hash
    if (crypto_hash_digest(&desc, sg, input_l, hash)){
        printk(KERN_INFO "Hash failed\n");
    }
    
    // Free hash
    crypto_free_hash(tfm);
    
    // Free memory pages
    for(i=0; i<TVMEM_NUM && tvmem[i];i++)
        free_page((unsigned long)tvmem[i]);
}

void defy_gen_key(const char *password, const int pass_length,
		  const char* salt, const int salt_length, u8 *key) {
    defy_hash(password, pass_length, salt, salt_length, key);
}


void defy_cipher(struct yaffs_dev *dev, u8 *buf, const int bufsize,
		 const int level, u8 *iv_byte, const int enc) {
    struct scatterlist sg[1];
    struct crypto_blkcipher *tfm;
    struct blkcipher_desc desc;
    u8 *key;
    char *tvmem[1];
    char iv[16];
    int err, iv_len;

    if(level == -1 && enc == DEFY_DECRYPT) {
	// DEFY: A level of -1 means no decryption
	return;
    }

    if(!buf) {
	// A NULL buffer means that we're deleting a chunk, we probably
	//    want to encrypt all 0xFF
	defy_printk("Buffer was empty\n", IN_F);
	defy_printk_indent();
	printk("Its size is listed as %d\n", bufsize);
	return;
    }

    // Allocate cipher
    tfm = crypto_alloc_blkcipher(DEFY_CRYPTO_FUNCTION, 0, CRYPTO_ALG_ASYNC);
    if (IS_ERR(tfm)){
        printk(KERN_INFO "Error allocating cipher\n");
        return;
    }
    
    desc.tfm = tfm;
    desc.flags = 0;
    
    // Allocate pages
    tvmem[0] = (void *)__get_free_page(GFP_KERNEL);
    if(!tvmem[0]) {
        printk(KERN_INFO "Could not allocate free page\n");
        crypto_free_blkcipher(tfm);
        return;
    }
    
    if(level == -1) {
	// DEFY: A level of -1 means random key for encryption
	key = kmalloc(DEFY_KEY_LENGTH, GFP_KERNEL);
	get_random_bytes(key, DEFY_KEY_LENGTH);
    } else {
	key = dev->defy_levels[level]->level_key;
    }

    // Initialize scatterlists
    sg_init_table(sg, 1);
    
    // Set scattterlist memory
    sg_set_buf(sg, tvmem[0], PAGE_SIZE);
    memset(tvmem[0], 0x00, PAGE_SIZE);
    memcpy(tvmem[0], buf, bufsize);
    
    // Set cipher key
    if(crypto_blkcipher_setkey(tfm, key, DEFY_KEY_LENGTH)) {
        printk("Key setting failed with flags %x\n",
               crypto_blkcipher_get_flags(tfm));
    }
    
    // Set the Initialization Vector
    iv_len = crypto_blkcipher_ivsize(tfm);
    memset(&iv, 0x00, iv_len);
    if(iv_len) {
        memcpy(&iv, iv_byte, iv_len < dev->iv_length ? iv_len : dev->iv_length);
        crypto_blkcipher_set_iv(tfm, iv, iv_len);
    }
    
    // Run cipher
    if(enc) {
        err = crypto_blkcipher_encrypt(&desc, sg, sg, bufsize);
    } else {
        err = crypto_blkcipher_decrypt(&desc, sg, sg, bufsize);
    }
    
    if(err) {
        printk(KERN_INFO "Encrypting/Decrypting failed with flags: %x\n",
               desc.flags);
    }
    
    // Copy cipher into original string
    memcpy(buf, tvmem[0], bufsize);
    
    // Free cipher
    crypto_free_blkcipher(tfm);
    
    // Free memory pages
    free_page((unsigned long)tvmem[0]);

    if(level == -1)
	kfree(key);
}

void defy_init_level(struct yaffs_dev *dev, struct defy_level_info *level,
		     const u8 *level_key) {
    memcpy(level->level_key, level_key, DEFY_KEY_LENGTH);
    level->cached_chunks = 0;
    level->block_to_write = -1;

    // DEFY: Create the write cache
    level->cache =
	kmalloc(dev->param.chunks_per_block *
		sizeof(struct defy_chunk), GFP_KERNEL);

    level->marker = kmalloc(sizeof(struct defy_marker), GFP_KERNEL);
    level->marker->bitmap = kmalloc((dev->internal_end_block -
			     dev->internal_start_block + 1) / 8,
			    GFP_KERNEL);
    memset(level->marker->bitmap, 0, (dev->internal_end_block -
				      dev->internal_start_block + 1) / 8);
}

void defy_finish_init_level(struct yaffs_dev *dev,
			    struct defy_level_info *level,
			    const u8 *marker,
			    struct defy_level_info *prev) {
    if(marker) {
	defy_printk("There is a marker", IN_F);
	return;
	if(!level) {
	    defy_printk("No level!", IN_F);
	    return;
	}
	if(!level->marker) {
	    defy_printk("No level->marker!", IN_F);
	    return;
	}
	memcpy(level->marker, marker, sizeof(struct defy_marker));
	level->marker->bitmap =
	    kmalloc((dev->internal_end_block -
		     dev->internal_start_block + 1) / 8,
		    GFP_KERNEL);
	memcpy(level->marker->bitmap, marker + sizeof(struct defy_marker),
	       (dev->internal_end_block - dev->internal_start_block + 1)
	       / 8);
	defy_printk_indent();
	printk("The level's name is %s\n", level->marker->level_id);
	defy_printk("The bitmap is:", IN_F);
	print_hex(level->marker->bitmap, (dev->internal_end_block -
					  dev->internal_start_block + 1) / 8);
    }
    
    return;
    level->prev_level = prev;
    if(prev) {
	level->next_level = prev->next_level;
	prev->next_level = level;
	if(level->next_level)
	    level->next_level->prev_level = level;
    } else {
	level->next_level = NULL;
    }
}

int defy_open_level(struct yaffs_dev *dev, const u8 *level_key,
		    struct defy_level_info *prev_level) {
    int marker_chunk, next_level_num, i;
    struct defy_level_info **temp_levels;
    struct defy_level_info *new_level;
    u8* marker_buffer;

    struct yaffs_ext_tags marker_tags;
    u8 empty_key[DEFY_KEY_LENGTH];

    memset(empty_key, 0, DEFY_KEY_LENGTH);
    if(!memcmp(level_key, empty_key, DEFY_KEY_LENGTH)) {
	prev_level->next_level = NULL;
	// DEFY: An empty key signifies no level
	return -1;
    }

    defy_printk("defy_open_level", BEGIN_F);

    for(i = 0; i < dev->num_levels; i++) {
	defy_printk("Comparing two keys", IN_F);
	print_hex(level_key, DEFY_KEY_LENGTH);
	print_hex(dev->defy_levels[i]->level_key, DEFY_KEY_LENGTH);
	if(!memcmp(level_key, dev->defy_levels[i]->level_key,
		   DEFY_KEY_LENGTH)) {
	    // DEFY: Key is already used, so level must already be open,
	    //         return that level to enable hierarchy
	    defy_printk("Level already in use", IN_F);
	    defy_printk("defy_open_level", END_F);
	    return i;
	}
    }

    // DEFY: Reallocate the defy_level_info array for another level
    temp_levels = kmalloc((dev->num_levels + 1) *
			  sizeof(struct defy_level_info *), GFP_KERNEL);
    if(dev->defy_levels) {
	memcpy(temp_levels, dev->defy_levels, dev->num_levels * 
	       sizeof(struct defy_level_info));
	memset(dev->defy_levels, 0, dev->num_levels *
	       sizeof(struct defy_level_info *));
	kfree(dev->defy_levels);
    }
    dev->defy_levels = temp_levels;
    dev->defy_levels[dev->num_levels] = kmalloc(sizeof(struct defy_level_info),
						GFP_KERNEL);
    new_level = dev->defy_levels[dev->num_levels];

    defy_init_level(dev, new_level, level_key);
    defy_printk("Level Key is:", IN_F);
    print_hex(new_level->level_key, DEFY_KEY_LENGTH);
    defy_printk_indent();
    printk("Looking for level %d\n", dev->num_levels);

    dev->defy_current_level = dev->num_levels;
    defy_printk("Starting the scan", IN_F);
    marker_chunk = defy_scan_backwards(dev);
    defy_printk("Finished the scan", IN_F);
    dev->defy_current_level = -1;
    // DEFY: if the level/password pair doesn't exist, exit
    if(marker_chunk < 1) {
	defy_destroy_level(dev, new_level);
	defy_printk("There was no level with this key", IN_F);
	defy_printk("defy_open_level", END_F);
	return marker_chunk;
    }

    marker_tags.defy_level = dev->num_levels;

    defy_printk_indent();
    printk("Getting marker data from chunk %d\n", marker_chunk);

    marker_buffer = yaffs_get_temp_buffer(dev);

    yaffs_rd_chunk_tags_nand(dev, marker_chunk,
			     marker_buffer, &marker_tags);

    defy_printk("Marker Hex", IN_F);
    print_hex(marker_buffer, sizeof(struct defy_marker));

    defy_printk("Bitmap Hex", IN_F);
    print_hex(marker_buffer + sizeof(struct defy_marker),
	      (dev->internal_end_block - dev->internal_start_block + 1) / 8);

    defy_printk("Tags Hex", IN_F);
    print_hex((u8 *)&marker_tags, sizeof(struct yaffs_ext_tags));

    // Works if I exit here
    if(!new_level) {
	defy_printk("No new_level!", IN_F);
	return 0;
    } else {
	defy_printk("New_level!", IN_F);
    }
    defy_finish_init_level(dev, new_level, marker_buffer, 
			   prev_level);
    defy_printk("defy_open_level", END_F);
    return dev->num_levels;

    new_level->defy_level = dev->num_levels;
    dev->num_levels++;

    defy_printk_indent();
    printk("There are %d levels\n", dev->num_levels);

    defy_printk("Next level key", IN_F);
    //print_hex(((struct defy_marker *)marker_data)->next_level_key,
    //	      DEFY_KEY_LENGTH);
    defy_printk("Marker Hex", IN_F);
    print_hex(marker_buffer, sizeof(struct defy_marker));

    next_level_num = -1;
    /*    
    next_level_num =
        defy_open_level(dev,
			((struct defy_marker *)marker_data)->next_level_key,
			new_level);
    */
    if(next_level_num > 0) {
	new_level->next_level = dev->defy_levels[next_level_num - 1];
	dev->defy_levels[next_level_num - 1]->prev_level = new_level;
    } else {
	new_level->next_level = NULL;
	dev->lowest_level = new_level->defy_level;
    }

    memset(marker_buffer, 0, dev->data_bytes_per_chunk);
    memset(&marker_tags, 0, sizeof(struct yaffs_ext_tags));
    //kfree(marker_buffer);
    yaffs_release_temp_buffer(dev, marker_buffer);
    printk("Opened level %d\n", new_level->defy_level);
    defy_printk("defy_open_level", END_F);
    defy_printk_indent();
    return new_level->defy_level;
}

int defy_create_level(struct yaffs_dev *dev, const char *level_name,
		      const int name_length, const u8 *password,
		      const int pass_length) {
    int i;
    int found_chunks = 0;
    u8 level_key[DEFY_KEY_LENGTH];
    struct defy_level_info **temp_levels;
    struct defy_level_info *new_level;
    struct yaffs_obj *root;
    struct yaffs_obj_hdr new_level_oh;
    struct yaffs_ext_tags tags;
    struct yaffs_block_info *bi;
    LIST_HEAD(hard_list);

    defy_printk("defy_create_level", BEGIN_F);

    if((prev_level_num == -1 && dev->num_levels != 0) ||
       prev_level_num >= dev->num_levels) {
	// Incorrect previous level chosen
	defy_printk("defy_create_level", END_F);
	return -ENOTDIR;
    }

    if(name_length > DEFY_ID_LENGTH) {
	// DEFY: Name too long
	defy_printk("defy_create_level", END_F);
	return -ENOTDIR;
    }

    defy_gen_key(password, pass_length, level_name, name_length,
		 level_key);

    if(defy_open_level(dev, level_key, NULL) >= 0) {
	// DEFY: Level exists
	memset(level_key, 0, DEFY_KEY_LENGTH);
	defy_printk("defy_create_level", END_F);
	return -EISDIR;
    }

    root = yaffs_find_or_create_by_number(dev, YAFFS_OBJECTID_ROOT,
    					  YAFFS_OBJECT_TYPE_DIRECTORY);
    if (!root) {
	// DEFY: Couldn't find root
	defy_printk("defy_create_level", END_F);
	return -EAGAIN;
    }

    // DEFY: Reallocate the defy_level_info array for another level
    temp_levels = kmalloc((dev->num_levels + 1) * 
			  sizeof(struct defy_level_info *), GFP_KERNEL);
    if(dev->defy_levels) {
	// There were already open levels, transfer them to the new array
	for(i = 0; i < dev->num_levels; i++) {
	    temp_levels[i] = dev->defy_levels[i];
	    dev->defy_levels[i] = NULL;
	}
	kfree(dev->defy_levels);
    }
    dev->defy_levels = temp_levels;
    dev->defy_levels[dev->num_levels] = kmalloc(sizeof(struct defy_level_info),
				      GFP_KERNEL);
    new_level = dev->defy_levels[dev->num_levels];

    defy_gen_key(password, pass_length, level_name, name_length, level_key);
    defy_init_level(dev, new_level, level_key);
    defy_printk("Level Key is:", IN_F);
    print_hex(new_level->level_key, DEFY_KEY_LENGTH);
    memset(level_key, 0, DEFY_KEY_LENGTH);

    memset(new_level->marker->level_id, '\0', DEFY_ID_LENGTH + 1);
    strcpy(new_level->marker->level_id, level_name);
    new_level->defy_level = dev->num_levels;

    // DEFY: Create an object header, put it in the cache for this
    //         level, then scan it as if it were being read from disk.
    //         This is how directories are created on mounting

    defy_format_level_oh(dev, root, &new_level_oh);
    strcpy(new_level_oh.name, new_level->marker->level_id);

    tags.chunk_used = 1;
    tags.obj_id = defy_new_obj_id(dev);
    tags.chunk_id = 0;
    tags.n_bytes = -1;
    tags.ecc_result = YAFFS_ECC_RESULT_NO_ERROR;
    tags.block_bad = 0;
    tags.extra_available = 0;
    tags.defy_level = dev->num_levels;

    defy_cache_chunk(dev, (u8 *)&new_level_oh, &tags);

    // Assign the seq_number to the bi just for the read, then convert back
    //   to -1.  This just allows yaffs2_scan_chunk to access the cache for
    //   the new level.
    bi = yaffs_get_block_info(dev, new_level->block_to_write);
    bi->defy_level = new_level->defy_level;
    if(defy_scan_chunk(dev, bi, new_level->block_to_write, 0, &found_chunks,
		       (u8 *)&new_level_oh, &hard_list, 0) == YAFFS_FAIL) {
	// DEFY: Level could not be created
	bi->defy_level = -1;
	defy_destroy_level(dev, new_level);
	memset(&new_level_oh, 0, sizeof(struct yaffs_obj_hdr));
	memset(&tags, 0, sizeof(struct yaffs_ext_tags));
	return -ENOMEM;
    }
    bi->defy_level = -1;

    // DEFY: Level was successfully created
    // DEFY: Put the new level in the appropriate place in the hierarchy
    if(prev_level_num == -1) {
	defy_finish_init_level(dev, new_level, NULL, NULL);
    } else {
	defy_finish_init_level(dev, new_level, NULL,
			       dev->defy_levels[prev_level_num]);
	prev_level_num = -1;
    }

    if(new_level->next_level == NULL) {
	// DEFY: This is the lowest level
	dev->lowest_level = dev->num_levels;
    }
    // DEFY: Increase the number of levels
    dev->num_levels++;

    defy_printk_indent();
    printk("There are now %d level(s)\n", dev->num_levels);
    defy_printk_indent();
    printk("The lowest level is %d\n", dev->lowest_level);

    memset(&new_level_oh, 0, sizeof(struct yaffs_obj_hdr));
    memset(&tags, 0, sizeof(struct yaffs_ext_tags));
    defy_printk("defy_create_level", END_F);
    return 0;
}

static ssize_t device_read(struct file *filp, char __user *buffer,
                           size_t length, loff_t *offset) {
    return simple_read_from_buffer(buffer, length, offset, msg, 18);
}

static ssize_t device_write(struct file *filp, const char __user *buff,
                            size_t len, loff_t *off) {
    if (len > 17)
        return -EINVAL;
    copy_from_user(msg, buff, len);
    msg[len] = '\0';
    return len;
}

long device_ioctl(struct file *filep, unsigned int cmd,
                  unsigned long arg) {
    u8 key[DEFY_KEY_LENGTH];
    int prev_num_levels;
    int len = 18;
    int pass_length = 0;
    int name_length = 0;
    //LIST_HEAD(level_list);
    
    switch(cmd) {
    case SET_PASSWORD:
        copy_from_user(msg, (char *)arg, len);
	strcpy(new_level_pass, msg);
        return 0;
    case SET_LEVEL_ID:
        copy_from_user(msg, (char *)arg, len);
	strcpy(new_level_name, msg);
        return 0;
    case GET_OPEN_LEVELS:
	//for(i = 0; i < num_levels; i++) {
	//  list_add(&level_list, defy_levels[i]->level_id);
	//}
	if(next_level_num == mydev->num_levels) {
	    copy_to_user((char *)arg, "\0", 1);
	    next_level_num = 0;
	}
	else {
	    strcpy(msg, mydev->defy_levels[next_level_num]->marker->level_id);
	    copy_to_user((char *)arg,
			 mydev->defy_levels[next_level_num]->marker->level_id,
			 DEFY_ID_LENGTH + 1);
	    next_level_num++;
	}
	return next_level_num;
    case SET_PREV_LEVEL:
	prev_level_num = (int) arg;
	if(prev_level_num >= mydev->num_levels || prev_level_num < 0)
	    return -ENOTDIR;
	return 0;
    case CREATE_LEVEL:
        for(name_length = 0; new_level_name[name_length]; name_length++);
        for(pass_length = 0; new_level_pass[pass_length]; pass_length++);
	return defy_create_level(mydev, new_level_name, name_length,
				 new_level_pass, pass_length); 
    case OPEN_LEVEL:
        for(name_length = 0; new_level_name[name_length]; name_length++);
        for(pass_length = 0; new_level_pass[pass_length]; pass_length++);
	defy_gen_key(new_level_pass, pass_length, new_level_name, name_length,
		     key);
	if(mydev == NULL)
	    defy_printk("mydev == null", IN_F);
	prev_num_levels = mydev->num_levels;
        defy_open_level(mydev, key, NULL);
	memset(key, 0, DEFY_KEY_LENGTH);
	if(mydev->num_levels == prev_num_levels)
	    return -EISDIR;
	else
	    return 0;
    default:
        return -ENOTTY;
    }
}

int defy_init_ioctl(void) {
    dev_t devno = MKDEV(0, 0);
    char *device_name = "defy";
    struct device *device;
    
    alloc_chrdev_region(&devno, 0, 1, device_name);
    dev_major = MAJOR(devno);
    dev_class = class_create(THIS_MODULE, device_name);
    cdev_init(&cdev, &fops);
    
    if(cdev_add(&cdev, devno, 1)) {
        printk("Error adding device\n");
        return -1;
    }
    
    device = device_create(dev_class, NULL, devno, NULL, device_name);
    if(IS_ERR(device)) {
        printk("Error creating device\n");
        return -1;
    }
    
    return 0;
}

int defy_exit_ioctl(void) {
    device_destroy(dev_class, MKDEV(dev_major, 0));
    cdev_del(&cdev);
    class_destroy(dev_class);
    return 0;
}

int defy_flush_levels(struct yaffs_dev *dev,
		      struct defy_level_info **level_info) {
    int blk, i;
    struct yaffs_block_info *bi;

    defy_printk("defy_flush_levels", BEGIN_F);

    if(dev->num_levels == 0) {
	defy_printk("defy_flush_levels", END_F);
	return 0;
    }

    for(i = 0; i < dev->num_levels; i++) {
    	defy_write_cache(dev, level_info[i]);
    }

    // DEFY: Set up the block for writing
    blk = defy_find_alloc_block(dev);
    bi = yaffs_get_block_info(dev, blk);
    yaffs_erase_block(dev, blk);
    bi->block_state = YAFFS_BLOCK_STATE_EMPTY;
    dev->alloc_block_finder = blk - 1;

    // DEFY: Write the marker to disk, followed by random chunks
    defy_write_marker(dev, level_info[dev->lowest_level]);
    defy_write_random_chunks(dev, 1);
    dev->n_erased_blocks--;
    dev->n_free_chunks -= dev->param.chunks_per_block;

    for(i = 0; i < dev->num_levels; i++) {
	defy_destroy_level(dev, level_info[i]);
    }

    defy_printk("defy_flush_levels", END_F);
    return 0;

    memset(level_info, 0, dev->num_levels * sizeof(struct defy_level_info *));
    kfree(level_info);
    level_info = NULL;

    defy_printk("defy_flush_levels", END_F);
    return 0;
}

int defy_init(struct yaffs_dev *dev) {
    defy_printk("defy_init", BEGIN_F);
    mydev = dev;
    dev->lowest_level = -1;
    dev->num_levels = 0;
    dev->iv_length = 16;
    if(defy_init_ioctl()) {
        return -1;
    }
    defy_printk("defy_init", END_F);
    return 0;
}

int defy_exit(struct yaffs_dev *dev) {
    int ret_val = 0;
    defy_printk("defy_exit", BEGIN_F);
    if(defy_exit_ioctl())
        ret_val = -1;
    if(defy_flush_levels(dev, dev->defy_levels))
	ret_val = -1;
    dev->lowest_level = -1;
    dev->num_levels = 0;
    defy_printk("defy_exit", END_F);
    return ret_val;
}

int defy_destroy_level(struct yaffs_dev *dev, struct defy_level_info *level) {
    defy_printk("defy_destroy_level", BEGIN_F);
    defy_printk_break();
    memset(level->marker->level_id, 0, DEFY_ID_LENGTH + 1);
    memset(level->level_key, 0, DEFY_KEY_LENGTH);
    level->next_level = NULL;
    level->prev_level = NULL;
    if(level->cache)
	kfree(level->cache);
    level->block_to_write = -1;
    memset(level->marker->bitmap, 0, (dev->internal_end_block -
				      dev->internal_start_block + 1) / 8);
    kfree(level->marker->bitmap);
    memset(level->marker, 0, sizeof(struct defy_marker));
    kfree(level->marker);
    kfree(level);
    defy_printk("defy_destroy_level", END_F);
    return 0;
}

int defy_add_block_to_level(struct yaffs_dev *dev,
			    struct defy_level_info *level, int blk) {
    int byte_offset = (blk - dev->internal_start_block) / 8;
    int bit_offset = (blk - dev->internal_start_block) % 8;
    u8 blocks_byte;

    defy_printk("defy_add_block_to_level", BEGIN_F);
    // DEFY: Create a byte with the appropriate bit set
    memset(&blocks_byte, 0x01, 1);
    blocks_byte = blocks_byte << bit_offset;
    // DEFY: OR the byte with the correlating byte in the bitmap
    level->marker->bitmap[byte_offset] |= blocks_byte;
    memset(&blocks_byte, 0, 1);

    defy_printk("Bitmap Hex", IN_F);
    print_hex(level->marker->bitmap,
	      (dev->internal_end_block - dev->internal_start_block + 1) / 8);
    defy_printk("defy_add_block_to_level", END_F);
    return 0;
}

int defy_remove_block_from_level(struct yaffs_dev *dev, int level_num,
				 int blk) {
    struct defy_level_info *level = dev->defy_levels[level_num];
    int byte_offset = (blk - dev->internal_start_block) / 8;
    int bit_offset = (blk - dev->internal_start_block) % 8;
    u8 blocks_byte;

    defy_printk("defy_remove_block_from_level", BEGIN_F);
    // DEFY: Create a byte with the appropriate bit set
    memset(&blocks_byte, 0x01, 1);
    blocks_byte = blocks_byte << bit_offset;
    // DEFY: XOR the byte with the correlating byte in the bitmap, effectively
    //         removing the appropriate block
    level->marker->bitmap[byte_offset] ^= blocks_byte;
    memset(&blocks_byte, 0, 1);
    defy_printk("defy_remove_block_from_level", END_F);

    return 0;
}

int defy_check_level_bitmap(struct yaffs_dev *dev,
			    struct defy_level_info *level, int blk) {
    int byte_offset = (blk - dev->internal_start_block) / 8;
    int bit_offset = (blk - dev->internal_start_block) % 8;
    int ret_val;
    u8 blocks_byte;

    memset(&blocks_byte, 0x01, 1);
    blocks_byte = blocks_byte << bit_offset;
    blocks_byte |= level->marker->bitmap[byte_offset];
    ret_val = memcmp(&level->marker->bitmap[byte_offset], &blocks_byte, 1);
    memset(&blocks_byte, 0, 1);
    return ret_val;
}

int defy_write_random_chunks(struct yaffs_dev *dev, int start_chunk) {
    int chunk;
    u8 *random_bytes = kmalloc(dev->data_bytes_per_chunk, GFP_KERNEL);
    struct yaffs_ext_tags *tags = kmalloc(sizeof(struct yaffs_ext_tags),
					  GFP_KERNEL);

    for(chunk = start_chunk; chunk < dev->param.chunks_per_block; chunk++) {
        get_random_bytes(random_bytes, dev->data_bytes_per_chunk);
	get_random_bytes(tags, sizeof(struct yaffs_ext_tags));
	tags->defy_level = -1;
	defy_write_new_chunk(dev, random_bytes, tags, 0);
    }

    kfree(random_bytes);
    kfree(tags);

    return 0;
}

int defy_find_alloc_block(struct yaffs_dev *dev) {
    int i;
    struct yaffs_block_info *bi;

    defy_printk("defy_find_alloc_block", BEGIN_F);

    if(dev->n_erased_blocks < 1) {
	// There are no free blocks to allocate
	return -1;
    }

    // Find a block of level -1, that means free so far as we know
    for (i = dev->internal_start_block; i <= dev->internal_end_block; i++) {
	dev->defy_block_finder++;
	if (dev->defy_block_finder < dev->internal_start_block
	    || dev->defy_block_finder > dev->internal_end_block) {
	    dev->defy_block_finder = dev->internal_start_block;
	}

	defy_printk_indent();
	printk("Checking block %d for allocation\n", dev->defy_block_finder);

	bi = yaffs_get_block_info(dev, dev->defy_block_finder);

	// DEFY: Just check to see if it hasn't been opened, as far as DEFY
	//         is concerned, that is a free block
	if (bi->defy_level == -1) {
	    bi->seq_number = dev->seq_number;
	    dev->n_erased_blocks--;
	    defy_printk_indent();
	    printk("Allocated block %d\n", dev->defy_block_finder);
	    defy_printk("defy_find_alloc_block", END_F);
	    return dev->defy_block_finder;
	} else {
	    defy_printk_indent();
	    printk("Block %d belongs to level %d\n", dev->defy_block_finder,
		   bi->defy_level);
	}
    }

    // Could not find a block of level -1
    defy_printk("defy_find_alloc_block", END_F);
    return -1;
}

int defy_cache_chunk(struct yaffs_dev *dev, const u8 *data,
		     struct yaffs_ext_tags *tags) {
    int ret_val;
    struct defy_level_info *level = dev->defy_levels[tags->defy_level];
    struct defy_chunk *chunk;
    
    defy_printk("defy_cache_chunk", BEGIN_F);
    defy_printk_indent();
    printk("Chunk at level %d\n", tags->defy_level);
    if(level->cached_chunks == 0) {
	level->block_to_write = defy_find_alloc_block(dev);
	level->seq_number =
	    yaffs_get_block_info(dev, level->block_to_write)->seq_number;
    }
    tags->seq_number = level->seq_number;
    chunk = &level->cache[level->cached_chunks];
    chunk->data =
	kmalloc(dev->data_bytes_per_chunk, GFP_KERNEL);
    chunk->tags =
	kmalloc(sizeof(struct yaffs_ext_tags), GFP_KERNEL);
    memcpy(chunk->data, data,
	   dev->param.total_bytes_per_chunk);
    memcpy(chunk->tags, tags, sizeof(struct yaffs_ext_tags));
    ret_val = (level->block_to_write * dev->param.chunks_per_block) +
	level->cached_chunks;
    level->cached_chunks++;
    if(level->cached_chunks == dev->param.chunks_per_block - 1) {
	// DEFY: Reserve the last chunk for the marker
	// DEFY: The block is full, write it to disk and clear the chunks
	defy_write_cache(dev, level);
    }
    defy_printk_indent();
    printk("Cached chunk %d\n", ret_val);
    defy_printk("defy_cache_chunk", END_F);
    return ret_val;
}

int defy_write_cache(struct yaffs_dev *dev, struct defy_level_info *level) {
    int chunk;
    struct yaffs_block_info *bi;

    defy_printk("defy_write_cache", BEGIN_F);
    // DEFY: Erase the appropriate block, ensuring that
    //         yaffs_find_alloc_block chooses the correct block
    defy_printk_indent();
    printk("Writing level %s\n", level->marker->level_id);
    if(level->cached_chunks == 0) {
	// DEFY: There are no chunks to write at this level
	defy_printk("There were no chunks to write", IN_F);
	defy_printk("defy_write_cache", END_F);
	return 0;
    }
    defy_printk_indent();
    printk("Writing %d chunks to block %d\n", level->cached_chunks,
	   level->block_to_write);

    bi  = yaffs_get_block_info(dev, level->block_to_write);

    // DEFY: Erase the intended block, then ensure YAFFS tries to write there
    //         first
    yaffs_erase_block(dev, level->block_to_write);
    bi->block_state = YAFFS_BLOCK_STATE_EMPTY;
    dev->alloc_block_finder = level->block_to_write - 1;

    // DEFY: Hook back into YAFFS infrastructure, writing normally to disk
    for(chunk = 0; chunk < level->cached_chunks; chunk++) {
	// DEFY: Write the chunk to disk, then erase it
	defy_write_new_chunk(dev, level->cache[chunk].data, 
			     level->cache[chunk].tags, 0);
	memset(level->cache[chunk].data, 0, dev->data_bytes_per_chunk);
	memset(level->cache[chunk].tags, 0, sizeof(struct yaffs_ext_tags));
	kfree(level->cache[chunk].data);
	kfree(level->cache[chunk].tags);
    }
    
    defy_add_block_to_level(dev, level, level->block_to_write);
    defy_printk("Writing marker chunk", IN_F);
    // DEFY: Write a marker chunk to enable hierarchical opening
    defy_write_marker(dev, level);

    // DEFY: Write random data to any remaining chunks
    defy_write_random_chunks(dev, level->cached_chunks + 1);

    bi->defy_level = level->defy_level;
    level->cached_chunks = 0;
    dev->n_erased_blocks--;
    dev->n_free_chunks -= dev->param.chunks_per_block;
    level->block_to_write = -1;
    defy_printk("defy_write_cache", END_F);
    return 0;
}

int defy_read_cached_chunk(struct yaffs_dev *dev, int nand_chunk, u8 *data,
			   struct yaffs_ext_tags *tags) {
    int chunk, flash_chunk;
    struct defy_level_info *level;

    if(tags->defy_level < 0)
	return -1;
    //defy_printk_indent();
    //printk("tags->defy_level is %d\n", tags->defy_level);
    level = dev->defy_levels[tags->defy_level];
    if(!level) {
	defy_printk("Error trying to access a NULL level", IN_F);
	return -1;
    }
    if(level->block_to_write == -1) {
	return -1;
    }

    defy_printk("defy_read_cached_chunk", BEGIN_F);
    
    defy_printk_indent();
    printk("Trying to read level %d\n", tags->defy_level);
    
    defy_printk_indent();
    printk("Checking chunks written to block %d\n",
	   level->block_to_write);

    flash_chunk = nand_chunk + dev->chunk_offset;
    defy_printk_indent();
    printk("Trying to read chunk %d\n", flash_chunk);

    if(level->block_to_write == flash_chunk / dev->param.chunks_per_block) {
	chunk = flash_chunk % dev->param.chunks_per_block;
	memcpy(data, level->cache[chunk].data, dev->data_bytes_per_chunk);
	memcpy(tags, level->cache[chunk].tags, sizeof(struct yaffs_ext_tags));
	defy_printk("defy_read_cached_chunk", END_F);    
	return 0;
    }
    defy_printk("defy_read_cached_chunk", END_F);    
    return -1;
}

void defy_write_marker(struct yaffs_dev *dev,
		       const struct defy_level_info *level) {
    u8 checksum[DEFY_KEY_LENGTH];
    u8 *marker_data = kmalloc(dev->data_bytes_per_chunk, GFP_KERNEL);
    struct yaffs_ext_tags *marker_tags = kmalloc(sizeof(struct yaffs_ext_tags),
						 GFP_KERNEL);

    memset(marker_data, 0, dev->data_bytes_per_chunk);
    memset(marker_tags, 0, sizeof(struct yaffs_ext_tags));
    marker_tags->chunk_used = 1;
    marker_tags->chunk_id = 0;
    marker_tags->defy_level = level->defy_level;
 
    // DEFY: Write the marker
    memcpy(marker_data, level->marker, sizeof(struct defy_marker));

    // DEFY: Then write the bitmap
    memcpy(marker_data + sizeof(struct defy_marker),
	   level->marker->bitmap, (dev->internal_end_block -
				   dev->internal_start_block + 1) / 8);

    // DEFY: Write a HMAC at the end of the chunk
    defy_hash(marker_data, dev->data_bytes_per_chunk - DEFY_KEY_LENGTH,
	      marker_data, DEFY_ID_LENGTH, checksum);
    memcpy(marker_data + (dev->data_bytes_per_chunk - DEFY_KEY_LENGTH),
	   checksum, DEFY_KEY_LENGTH);

    defy_write_new_chunk(dev, marker_data, marker_tags, 0);

    memset(marker_data, 0, dev->data_bytes_per_chunk);
    memset(marker_tags, 0, sizeof(struct yaffs_ext_tags));
    kfree(marker_data);
    kfree(marker_tags);
}

int defy_verify_marker(const struct yaffs_dev *dev, u8 *marker_data) {
    u8 checksum[DEFY_KEY_LENGTH];

    defy_hash(marker_data, dev->data_bytes_per_chunk - DEFY_KEY_LENGTH,
	      marker_data, DEFY_ID_LENGTH, checksum);
    return memcmp(checksum, marker_data +
		  (dev->data_bytes_per_chunk - DEFY_KEY_LENGTH),
		  DEFY_KEY_LENGTH);
}

int defy_find_marker(struct yaffs_dev *dev, int blk, int level) {
    int offset;
    int start_chunk = blk * dev->param.chunks_per_block;
    u8 checksum[DEFY_KEY_LENGTH];
    u8 *marker_data = kmalloc(dev->data_bytes_per_chunk, GFP_KERNEL);
    struct yaffs_ext_tags *marker_tags =
	kmalloc(sizeof(struct yaffs_ext_tags), GFP_KERNEL);

    //defy_printk("defy_find_marker", BEGIN_F);
    marker_tags->defy_level = level;
    // DEFY: Scan each chunk, the last chunk with the same seq_number is the
    //         marker
    for(offset = dev->param.chunks_per_block - 1; offset >= 0; offset--) {
	yaffs_rd_chunk_tags_nand(dev, start_chunk + offset,
				 marker_data, marker_tags);
	if(!defy_verify_marker(dev, marker_data)) {
	    defy_hash(marker_data,
		      dev->data_bytes_per_chunk - DEFY_KEY_LENGTH,
		      marker_data, DEFY_KEY_LENGTH, checksum);
	    defy_printk("Found a marker, checksum comparison is:", IN_F);
	    print_hex(marker_data +
		  (dev->data_bytes_per_chunk - DEFY_KEY_LENGTH),
		  DEFY_KEY_LENGTH);
	    print_hex(checksum, DEFY_KEY_LENGTH);
	    memset(marker_data, 0, dev->data_bytes_per_chunk);
	    memset(marker_tags, 0, sizeof(struct yaffs_ext_tags));
	    kfree(marker_data);
	    kfree(marker_tags);
	    //defy_printk("defy_find_marker", END_F);
	    return offset;
	}
	/*
	if(offset == 0) {
	    defy_printk("Did not find a marker, checksums compare is:", IN_F);
	    defy_hash(marker_data,
		      dev->data_bytes_per_chunk - DEFY_KEY_LENGTH,
		      marker_data, DEFY_KEY_LENGTH, checksum);
	    print_hex(marker_data +
		  (dev->data_bytes_per_chunk - DEFY_KEY_LENGTH),
		  DEFY_KEY_LENGTH);
	    print_hex(checksum, DEFY_KEY_LENGTH);
	}
	*/
    }

    // DEFY: Could not find a valid marker
    memset(marker_data, 0, dev->data_bytes_per_chunk);
    memset(marker_tags, 0, sizeof(struct yaffs_ext_tags));
    kfree(marker_data);
    kfree(marker_tags);
    //defy_printk("defy_find_marker", END_F);
    return -1;
}

void defy_format_level_oh(struct yaffs_dev *dev, struct yaffs_obj *root,
			    struct yaffs_obj_hdr *oh) {
	oh->type = YAFFS_OBJECT_TYPE_DIRECTORY;

	oh->parent_obj_id = YAFFS_OBJECTID_ROOT;

	oh->yst_mode = root->yst_mode;
	oh->yst_uid = root->yst_uid;
	oh->yst_gid = root->yst_gid;
	oh->yst_atime = root->yst_atime;
	oh->yst_mtime = root->yst_mtime;
	oh->yst_ctime = root->yst_ctime;

	oh->file_size = -1;

	oh->equiv_id = -1;

	oh->yst_rdev = root->yst_rdev;

	oh->inband_shadowed_obj_id = -1;
	oh->inband_is_shrink = -1;

	oh->shadows_obj = -1;	/* This object header shadows the
				specified object if > 0 */

	oh->is_shrink = -1;
}

// Password Based Key Derivation Function 2 (PBKDF2) functions

static void pkcs5_initial_prf(unsigned char *p, size_t plen,
			      unsigned char *salt, size_t saltlen, size_t i,
			      unsigned char *out, size_t *outlen) {
    
}

static void pkcs5_subsequent_prf(unsigned char *p, size_t plen,
				 unsigned char *v, size_t vlen,
				 unsigned char *o, size_t *olen) {
    
}

static void pkcs5_F(unsigned char *p, size_t plen, unsigned char *salt,
		    size_t saltlen, size_t ic, size_t bix,
		    unsigned char *out) {
    size_t i = 1;
    size_t j, outlen;
    unsigned char ulast[PRF_OUT_LEN];
    
    memset(out, 0, PRF_OUT_LEN);
    pkcs5_initial_prf(p, plen, salt, saltlen, bix, ulast, &outlen);
    while(i++ <= ic) {
	for(j = 0; j < PRF_OUT_LEN; j++)
	    out[j] ^= ulast[j];
	pkcs5_subsequent_prf(p, plen, ulast, PRF_OUT_LEN, ulast, &outlen);
    }
    for(j = 0; j < PRF_OUT_LEN; j++)
	out[j] ^= ulast[j];
}
	
int spc_pbkdf2(unsigned char *pw, unsigned int pwlen, char *salt,
	       unsigned long long saltlen, unsigned int ic, unsigned char *dk,
	       unsigned long long dklen) {
    unsigned long i, l, r;
    unsigned char final[PRF_OUT_LEN] = {0, };
    
    if(dklen > ((((unsigned long long)1) << 32) - 1) * PRF_OUT_LEN) {
	return -ENOEXEC;
    }
	
    l = dklen;
    r = do_div(l, PRF_OUT_LEN);
    for(i = 0; i < l; i++)
	pkcs5_F(pw, pwlen, salt, saltlen, ic, i, dk + (i - 1) * PRF_OUT_LEN);
    if(r) {
	pkcs5_F(pw, pwlen, salt, saltlen, ic, i, final);
	for(l = 0; l < r; l++)
	    *(dk + (i - 1) * PRF_OUT_LEN + 1) = final[l];
    }

    return 0;
}
